﻿// ## 生成滑动列表必须以下步骤：
// 1. 持有RecycleView对象rv，rv.Init(callBackFunc)
// 2. 刷新整个列表（首次调用和数量变化时调用）: ShowList(int count)
// 3. 回调: Func(GameObject cell, int index) 
// ----------
// 功能接口看代码，案例详见RecycleViewTest.cs
// 刷新单个项: UpdateCell(int index)
// 刷新列表数据(无数量变化时调用): UpdateList()
// 定位到索引所在当前列表的位置 GoToCellPos(int index)

using UnityEngine;
using UnityEngine.UI;
using System.Collections.Generic;
using UnityEngine.EventSystems;
using System;
using System.Text;
using UnityEngine.Serialization;

namespace WenRuo
{
    public enum E_Direction
    {
        Horizontal,
        Vertical
    }


    public class RecycleView : MonoBehaviour, IBeginDragHandler, IEndDragHandler, IDragHandler
    {
        public GameObject firstArrow;
        public GameObject endArrow;
        public E_Direction dir = E_Direction.Vertical;

        public int rowOrColumnCount = 1; // 默认显示1行
        public float squareSpacing = 5f; // 方阵间距
        public float spaceX = 0f; // 行间距
        public float spaceY = 0f; // 列间距
        public float paddingTop = 0f; // 顶部空隙
        public float paddingLeft = 0f; // 左侧空隙
        public float paddingRight = 0f; // 右部空隙
        public float paddingBottom = 0f; // 底部空隙


        protected ScrollRect scrollRect;
        protected RectTransform scrollRectTrans;
        protected float planeW;
        protected float planeH;

        protected GameObject content;
        protected RectTransform contentRectTrans;
        protected float contentW;
        protected float contentH;

        public GameObject cellPfefb; //指定的cell
        protected float cellW;
        protected float cellH;


        protected Action<GameObject, int> FuncCallBackFunc;
        protected Action<GameObject, int> FuncOnClickCallBack;
        protected Action<int, bool, GameObject> FuncOnButtonClickCallBack;


        [SerializeField] protected int dataCount = -1; //列表数量
        [SerializeField] protected int minIndex = -1;
        [SerializeField] protected int maxIndex = -1;

        [Serializable]
        //记录 物体的坐标 和 物体 
        protected struct CellInfo
        {
            public Vector3 pos;
            public GameObject obj;
        };

        [SerializeField] protected CellInfo[] cellInfos;
        protected bool isClearList = false; //是否清空列表

        // 对象池
        protected Stack<GameObject> Pool = new Stack<GameObject>();
        protected bool isInited = false;
        private bool isInit = false;

        protected void OnDestroy()
        {
            DisposeAll();
        }

        protected void DisposeAll()
        {
            if (FuncCallBackFunc != null) FuncCallBackFunc = null;
            if (FuncOnClickCallBack != null) FuncOnClickCallBack = null;
            if (FuncOnButtonClickCallBack != null) FuncOnButtonClickCallBack = null;
        }

        /// <summary>
        /// 初始化
        /// </summary>
        /// <param name="callBack"></param>
        /// <param name="onClickCallBack"></param>
        /// <param name="onButtonClickCallBack"></param>
        public virtual void Init(Action<GameObject, int> callBack = null,
            Action<GameObject, int> onClickCallBack = null,
            Action<int, bool, GameObject> onButtonClickCallBack = null)
        {
            DisposeAll();

            if (callBack != null)
                FuncCallBackFunc = callBack;

            if (onClickCallBack != null)
                FuncOnClickCallBack = onClickCallBack;

            if (onButtonClickCallBack != null)
                FuncOnButtonClickCallBack = onButtonClickCallBack;

            if (isInit) return;

            //ScrollRect 
            scrollRectTrans = GetComponent<RectTransform>();
            planeW = scrollRectTrans.rect.width;
            planeH = scrollRectTrans.rect.height;

            scrollRect = GetComponent<ScrollRect>();
            scrollRect.onValueChanged.RemoveAllListeners();

            //添加滑动事件
            scrollRect.onValueChanged.AddListener(delegate(Vector2 value) { ScrollRectListener(value); });
            if (firstArrow != null || endArrow != null)
            {
                scrollRect.onValueChanged.AddListener(delegate(Vector2 value) { OnDragListener(value); });
                OnDragListener(Vector2.zero);
            }

            //Content
            content = scrollRect.content.gameObject;
            contentRectTrans = content.GetComponent<RectTransform>();
            contentH = contentRectTrans.rect.height;
            contentW = contentRectTrans.rect.width;

            contentRectTrans.pivot = new Vector2(0f, 1f);
            CheckAnchor(contentRectTrans);

            // cell
            if (cellPfefb == null)
                cellPfefb = content.transform.GetChild(0).gameObject;
            // SetPoolsObj(cellPfefb);
            RectTransform cellRectTrans = cellPfefb.GetComponent<RectTransform>();
            cellRectTrans.pivot = new Vector2(0f, 1f);
            CheckAnchor(cellRectTrans);
            cellRectTrans.anchoredPosition = Vector2.zero;
            cellH = cellRectTrans.rect.height;
            cellW = cellRectTrans.rect.width;

            // 记录间距信息 如果存在行列设置就引用，没有使用方阵间距
            if (spaceX == 0 && spaceY == 0)
                spaceX = spaceY = squareSpacing;
            else
                squareSpacing = 0;


            if (dir == E_Direction.Vertical)
            {
                rowOrColumnCount = (int)((planeW - paddingLeft - paddingRight + spaceX) / (cellW + spaceX));
            }
            else
            {
                rowOrColumnCount = (int)((planeH - paddingTop - paddingBottom + spaceY) / (cellH + spaceY));
            }

            isInit = true;
        }

        private void CheckAnchor(RectTransform rectTrans) // 检查 Anchor 是否正确
        {
            if (dir == E_Direction.Vertical)
            {
                if (!((rectTrans.anchorMin == new Vector2(0, 1) && rectTrans.anchorMax == new Vector2(0, 1)) ||
                      (rectTrans.anchorMin == new Vector2(0, 1) && rectTrans.anchorMax == new Vector2(1, 1))))
                {
                    rectTrans.anchorMin = new Vector2(0, 1);
                    rectTrans.anchorMax = new Vector2(1, 1);
                }
            }
            else
            {
                if (!((rectTrans.anchorMin == new Vector2(0, 1) && rectTrans.anchorMax == new Vector2(0, 1)) ||
                      (rectTrans.anchorMin == new Vector2(0, 0) && rectTrans.anchorMax == new Vector2(0, 1))))
                {
                    rectTrans.anchorMin = new Vector2(0, 0);
                    rectTrans.anchorMax = new Vector2(0, 1);
                }
            }
        }


        #region 事件

        // 滑动事件
        protected virtual void ScrollRectListener(Vector2 value)
        {
            UpdateCheck();
        }

        protected void OnDragListener(Vector2 value)
        {
            float normalizedPos = dir == E_Direction.Vertical
                ? scrollRect.verticalNormalizedPosition
                : scrollRect.horizontalNormalizedPosition;

            if (dir == E_Direction.Vertical)
            {
                if (contentH - planeH < 10)
                {
                    SetActive(firstArrow, false);
                    SetActive(endArrow, false);
                    return;
                }
            }
            else
            {
                if (contentW - planeW < 10)
                {
                    SetActive(firstArrow, false);
                    SetActive(endArrow, false);
                    return;
                }
            }

            if (normalizedPos >= 0.9)
            {
                SetActive(firstArrow, false);
                SetActive(endArrow, true);
            }
            else if (normalizedPos <= 0.1)
            {
                SetActive(firstArrow, true);
                SetActive(endArrow, false);
            }
            else
            {
                SetActive(firstArrow, true);
                SetActive(endArrow, true);
            }
        }

        public virtual void OnBeginDrag(PointerEventData eventData)
        {
        }

        public virtual void OnDrag(PointerEventData eventData)
        {
        }

        public virtual void OnEndDrag(PointerEventData eventData)
        {
        }

        #endregion

        #region 公开

        public virtual void ShowList(string numStr)
        {
        }

        /// <summary>
        /// 显示数据 量
        /// </summary>
        /// <param name="showCount"></param>
        public virtual void ShowList(int showCount)
        {
            
            SetContentRect(showCount, dataCount);
         

            //-> 计算 开始索引
            int lastEndIndex = 0;

            //-> 过多的物体 扔到对象池 ( 首次调 ShowList函数时 则无效 )
            if (isInited)
            {
                lastEndIndex = showCount - dataCount > 0 ? dataCount : showCount;
                lastEndIndex = isClearList ? 0 : lastEndIndex;

                int count = isClearList ? cellInfos.Length : dataCount;
                for (int i = lastEndIndex; i < count; i++)
                {
                    if (cellInfos[i].obj != null)
                    {
                        SetPoolsObj(cellInfos[i].obj);
                        cellInfos[i].obj = null;
                    }
                }
            }


            CellInfo[] tempCellInfos = cellInfos;
            cellInfos = new CellInfo[showCount];

            //-> 1: 计算 每个Cell坐标并存储 2: 显示范围内的 Cell
            for (int i = 0; i < showCount; i++)
            {
                // * -> 存储 已有的数据 ( 首次调 ShowList函数时 则无效 )
                if (dataCount != -1 && i < lastEndIndex)
                {
                    CellInfo tempCellInfo = tempCellInfos[i];
                    //-> 计算是否超出范围 
                    if (!IsOutRange(tempCellInfo.pos))
                    {
                        if (tempCellInfo.obj == null)
                            tempCellInfo.obj = GetPoolsObj();
                        SetGameObject(tempCellInfo.obj, tempCellInfo.pos, i.ToString());
                    }
                    else
                    {
                        SetPoolsObj(tempCellInfo.obj);
                        tempCellInfo.obj = null;
                    }

                    cellInfos[i] = tempCellInfo;
                    continue;
                }

               // new Cell

                CellInfo cellInfo = new CellInfo(); 
                cellInfo.pos = GetCellPos(i);  //计算每个Cell坐标 
 
                if (IsOutRange(cellInfo.pos)) //-> 计算是否超出范围 
                {
                    cellInfo.obj = null;
                    cellInfos[i] = cellInfo;
                    continue;
                }

                cellInfo.obj = GetPoolsObj();
                cellInfos[i] = cellInfo;

                SetGameObject(cellInfo.obj, cellInfo.pos, i.ToString());

             
            }

            dataCount = showCount;
            isInited = true;

            OnDragListener(Vector2.zero);
            UpdateCheck();
        }

       
        protected void SetContentRect(int showCount, int dataCount) //计算 Content 尺寸
        {
            int rowCount = 0;
            int columnCount = 0;

            if (dir == E_Direction.Vertical)
            {
                columnCount = rowOrColumnCount;
                rowCount = Mathf.CeilToInt((float)showCount / rowOrColumnCount);

                contentW = columnCount * cellW + spaceX * (columnCount - 1) + paddingLeft + paddingRight;
                contentH = rowCount * cellH + spaceY * (rowCount - 1) + paddingTop + paddingBottom;

                contentH = contentH < planeH ? planeH : contentH;

                contentRectTrans.sizeDelta = new Vector2(contentW, contentH);
                if (showCount != dataCount)
                    contentRectTrans.anchoredPosition = new Vector2(contentRectTrans.anchoredPosition.x, 0);
            }
            else
            {
                rowCount = rowOrColumnCount;
                columnCount = Mathf.CeilToInt((float)showCount / rowOrColumnCount);

                contentW = columnCount * cellW + spaceX * (columnCount - 1) + paddingLeft + paddingRight;
                contentH = rowCount * cellH + spaceY * (rowCount - 1) + paddingTop + paddingBottom;

                contentW = contentW < planeW ? planeW : contentW;

                contentRectTrans.sizeDelta = new Vector2(contentW, contentH);
                if (showCount != dataCount)
                    contentRectTrans.anchoredPosition = new Vector2(0, contentRectTrans.anchoredPosition.y);
            }
        }

        protected Vector3 GetCellPos(int _index)  //计算每个Cell坐标 
        {
            Vector3 pos2 = Vector3.zero;
            int row = 0;
            int column = 0;

            if (dir == E_Direction.Vertical)
            {
                row = _index % rowOrColumnCount;
                column = Mathf.FloorToInt(_index / rowOrColumnCount);
            }
            else
            {
                row = Mathf.FloorToInt(_index / rowOrColumnCount);
                column = _index % rowOrColumnCount;
            }

            pos2.x = row * cellW + spaceX * row + paddingLeft;
            pos2.y = column * cellH + spaceY * column + paddingTop;
            pos2.y = -pos2.y;

            return pos2;
        }

        /// <summary>
        /// 下标
        /// </summary>
        /// <param name="pos"></param>
        /// <returns></returns>
        public int GetCellIndex(Vector3 pos)
        {
            float posX = Mathf.Abs(pos.x);
            float posY = Mathf.Abs(pos.y);

            int Column = (int)((posX - paddingLeft + spaceX) / (cellW + spaceX));

            int row = (int)((posY - paddingTop + spaceY) / (cellH + spaceY));
            if (dir == E_Direction.Vertical)
            {
                return row * rowOrColumnCount + Column;
            }
            else
            {
                return row + Column * rowOrColumnCount;
            }
        }

        /// <summary>
        /// 定位到某一单元格的坐标位置
        /// </summary>
        /// <param name="index"></param>
        public void GoToCellPos(int index)
        {
            // 如果cellInfo不存在坐标，说明没有被初始化过，当前没有数据，直接return
            if (cellInfos.Length == 0) return;

            // 当前索引所在行的第一个索引

            int theFirstIndex = index - index % rowOrColumnCount;

            int offectIndex = index - theFirstIndex;

            if (theFirstIndex >= dataCount)
            {
                int pix = dataCount % rowOrColumnCount;
                offectIndex = ((pix != 0) ? pix : rowOrColumnCount);
                theFirstIndex = dataCount - offectIndex;
                offectIndex -= 1;
            }

            float maxPosX = contentW - planeW;
            float maxPosY = contentH - planeH;

            if (maxPosY < 0)
                maxPosY = 0;

            if (maxPosX < 0)
                maxPosX = 0;

            Vector2 newPos = cellInfos[theFirstIndex].pos;
            float posX;
            float posY;
            if (dir == E_Direction.Vertical)
            {
                posX = -(Mathf.Abs(newPos.x) + offectIndex * cellW + offectIndex * spaceX);
                posY = Mathf.Abs(newPos.y);
            }
            else // 横向滑动时
            {
                posX = -(Mathf.Abs(newPos.x));
                posY = (Mathf.Abs(newPos.y) + offectIndex * cellH + offectIndex * spaceY);
            }

            maxPosX = -maxPosX;
            posX = Mathf.Max(posX, maxPosX);
            posY = Mathf.Min(posY, maxPosY);

            contentRectTrans.anchoredPosition = new Vector2(posX, posY);
        }

        /// <summary>
        /// 刷新列表
        /// </summary>
        public virtual void UpdateList()
        {
            for (int i = 0, length = cellInfos.Length; i < length; i++)
            {
                UpdateCell(i, true);
            }
        }

        /// <summary>
        /// 刷新某一项
        /// </summary>
        /// <param name="index">下标 0、1</param>
        /// <param name="isUpdate"></param>
        public void UpdateCell(int index, bool isUpdate = false)
        {
            CellInfo cellInfo = cellInfos[index];
            if (cellInfo.obj != null)
            {
                if (!IsOutRange(cellInfo.pos))
                {
                    Func(FuncCallBackFunc, cellInfo.obj, isUpdate);
                }
            }
        }

        #endregion


        protected void SetminMaxIndex() // 开始结束 下标
        {
            endPos = contentRectTrans.anchoredPosition;
            minIndex = GetCellIndex(endPos); 
            
            endPos.x = Math.Abs(endPos.x) + planeW;
            endPos.y = Math.Abs(endPos.y) + planeH;
            maxIndex = GetCellIndex(endPos);
            
            maxIndex -= 1;
            if (maxIndex < 0)
                maxIndex = 0;
            maxIndex = Mathf.Min(maxIndex, dataCount); 
        }

        #region 显示区域 刷新

        private Vector3 endPos;

        protected virtual void UpdateCheck()
        {
            if (cellInfos == null) return;

            // 开始结束 下标
            SetminMaxIndex();


            // 检查超出范围
            for (int i = 0, length = cellInfos.Length; i < length; i++)
            {
                CellInfo cellInfo = cellInfos[i];
                GameObject obj = cellInfo.obj;
                Vector3 pos = cellInfo.pos;

                // 判断是否超出显示范围
                if (IsOutRange(pos))
                {
                    // 把超出范围的cell 扔进 poolsObj里
                    if (obj != null)
                    {
                        SetPoolsObj(obj);
                        cellInfos[i].obj = null;
                    }
                }
                else
                {
                    if (obj == null)
                    {
                        // 优先从 poolsObj中 取出 （poolsObj为空则返回 实例化的cell）
                        GameObject cell = GetPoolsObj();
                        cellInfos[i].obj = cell;
                        SetGameObject(cell, pos, i.ToString());
                    }
                }
            }
        }

        protected bool IsOutRange(Vector3 pos) // 判断是否超出显示范围
        {
            float rPos = dir == E_Direction.Vertical ? pos.y : pos.x;
            return IsOutRange(rPos);
        }

        protected bool IsOutRange(float pos)
        {
            Vector3 contentPos = contentRectTrans.anchoredPosition;
            contentPos.y = Math.Abs(contentPos.y);
            contentPos.x = Math.Abs(contentPos.x);
            pos = Math.Abs(pos);

            if (dir == E_Direction.Vertical)
            {
                if (pos + cellH < contentPos.y || pos - planeH > contentPos.y)
                {
                    return true;
                }
            }
            else
            {
                if (pos + cellW < contentPos.x || pos - planeW > contentPos.x)
                {
                    return true;
                }
            }

            return false;
        }

        //回调
        protected void Func(Action<GameObject, int> func, GameObject selectObject, bool isUpdate = false)
        {
            int index = int.Parse(selectObject.name);
            if (func != null)
                func(selectObject, index);
        }

        protected virtual void SetGameObject(GameObject cell, Vector3 localPos, string name)
        {
            // 记录bug，这里应该使用localPosition，避免z轴丢失导致刷新列表的时候z轴异常 为什么z轴不为0 未知
            // cell.transform.GetComponent<RectTransform>().anchoredPosition = cellInfo.pos;
            cell.transform.GetComponent<RectTransform>().localPosition = localPos;
            cell.gameObject.name = name;
            cell.SetActive(true);

            //-> 回调  函数
            Func(FuncCallBackFunc, cell);
        }

        protected void SetActive(GameObject obj, bool isActive)
        {
            if (obj != null)
            {
                obj.SetActive(isActive);
            }
        }

        #endregion

        #region pool cell

        //取出 cell
        protected virtual GameObject GetPoolsObj()
        {
            GameObject cell = null;
            if (Pool.Count > 0)
                cell = Pool.Pop();

            if (cell == null)
                cell = Instantiate(this.cellPfefb) as GameObject;

            cell.transform.SetParent(content.transform);
            cell.transform.localScale = Vector3.one;
            SetActive(cell, true);

            return cell;
        }

        //存入 cell
        protected virtual void SetPoolsObj(GameObject cell)
        {
            if (cell != null)
            {
                Pool.Push(cell);
                SetActive(cell, false);
            }
        }

        #endregion


        #region Expand  函数

        public virtual void OnClickCell(GameObject cell)
        {
        }

        public virtual void OnClickExpand(int index)
        {
        }

        public virtual void SetToPageIndex(int index)
        {
        }

        #endregion


#if UNITY_EDITOR
        public void LogRecycleView()
        {
            StringBuilder bulider = new StringBuilder();
            // 拿到容器基础信息
            bulider.AppendLine("----------------------------------------------------------------------------");
            bulider.AppendLine("Direction: " + dir);
            bulider.AppendLine("Lines: " + rowOrColumnCount);
            bulider.AppendLine(string.Format("minIndex: {0} , maxIndex: {1}", minIndex, maxIndex));
            bulider.AppendLine("Capacity: " + (maxIndex - minIndex + 1));
            bulider.AppendLine("----------------------------------------------------------------------------");

            Debug.Log(bulider.ToString());
        }
#endif
    }
}